2.6 RMI over IIOP

rmic can now generate stubs for the Internet Inter-ORB protocol (IIOP) which is normally used by CORBA:

$ cd iiop/time
$ CLASSPATH=../.. javac TimeService.java
$ rmic -classpath ../.. -keepgenerated -iiop -d ../.. iiop.time.TimeService

Time and TimeService are unchanged except that the package is now iiop.time .

The proxy classes are _TimeService_Tie , required by the server, and _Time_Stub , required by server and client.

Client is changed because a reference to the service must now be obtained from a CORBA compliant name service:


iiop/time/Client.java
package iiop.time;
import iiop.rmi.Server;
/** <tt>java iiop.time.Client</tt><br>
    connects to time service.
  */
public class Client {
  public static void main (String args []) {
    try {
      System.out.println(((Time)Server.lookup(Time.id, Time.class)).getTime());
    } catch (Exception e) { System.err.println(e); System.exit(1); }
  }
}
 


A new, reasonably generic rmi.Server class is implemented to support the lookup:


iiop/rmi/Server.java
package iiop.rmi;
import java.rmi.Remote;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
/** <tt>java iiop.rmi.Server service</tt><br>
    creates service(), binds it for service.id.
    Set <tt>rmi.log</tt> to display threads.
  */
public class Server {
  /** locates a service using JNDI.
      @param id service's registry name.
    */
  public static Object lookup (String id, Class to) throws NamingException {
    return PortableRemoteObject.narrow(new InitialContext().lookup(id), to);
  }
 


This lookup uses the Java Naming and Directory Interface (JNDI) with a plugin for the CORBA naming service.

rmi.Server is designed to register and start a service:


iiop/rmi/Server.java
  /** starts and registers a service using JNDI.
      Set <tt>rmi.log</tt> to show threads.
      @param args [0] classname of service; class must contain static String id.
   */
  public static void main (String args []) throws Exception {
    Class serviceClass = null;
    try {
      if (args != null && args.length > 0) {
        serviceClass = Class.forName(args[0]);
        Remote service = (Remote)serviceClass.newInstance();
        PortableRemoteObject.exportObject(service);
        new InitialContext().rebind((String)serviceClass.getField("id").get(null),
                                                                          service);
        if (Boolean.getBoolean("rmi.log"))
          dumpThreads(true);
      }
   }  finally {
      if (serviceClass == null)
        System.err.println("usage: java iiop.rmi.Server service");
    }
  }
 


It is easy to see that InitialContext does for JNDI what Naming does for RMI. dumpThreads() is unchanged from before.

Deployment

tnameserv implements a transient CORBA-compliant name service:

$ tnameserv -ORBInitialPort 1111 &
Initial Naming Context:
IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f6e746578743a312e3000000000010000000000000050000101000000000a3132372e302e302e3200042b00000018afabcafe00000002135175b00000000800000000000000000000000100000001000000140000000000010020000000000001010000000000
TransientNameServer: setting port for initial object references to: 1111
Ready.

The output could be used to provide a CORBA client with access to the name service. JNDI, however, can also deal with a URL-style reference:

$ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \
>   -Djava.naming.provider.url=iiop://localhost:1111 \
>   iiop.rmi.Server iiop.time.TimeService &
$ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \
>   -Djava.naming.provider.url=iiop://localhost:1111 \
>   iiop.time.Client
Mon May 28 22:58:58 CEST 2001

Server and client need suitable JNDI plugins to reach tnameserv .

Callbacks

Both, the arithmetic service and the chat service, can be run over IIOP. However, the client has to do a little bit of extra work to set up the callback:


iiop/cpu/Client.java
package iiop.cpu;
import iiop.rmi.Server;
import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
/** <tt>java iiop.cpu.Client</tt><br>
    connects to cpu service, provides terms.
  */
public class Client implements Terms, Remote {
 
  // implementation of Terms as before
 
  public static void main (String args []) {
    try {
      Cpu cpu = (Cpu)Server.lookup(Cpu.id, Cpu.class);
      Client client = new Client();
      PortableRemoteObject.exportObject(client);
      client.run(cpu);
      PortableRemoteObject.unexportObject(client);
    } catch (Exception e) { System.out.println(e); System.exit(1); }
    if (Boolean.getBoolean("rmi.log"))
      Server.dumpThreads(true);
  }
}
 


Client implements Terms and needs to be exported by proxy to the arithmetic service. This requires essentially a delegate pattern which is managed using exportObject() and unexportObject() from PortableRemoteObject .

The threads are implemented correctly, the client terminates properly. However, the arithmetic service once again receives a stub rather than itself in result() :

$ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \
>   -Djava.naming.provider.url=iiop://localhost:1111 iiop.cpu.Client
service: iiop.cpu.CpuService@4b222f != IOR:0000000000000029524d493a69696f702e6370752e437075536572766963653a3030303030303030303030303030303000000000000000010000000000000050000101000000000a3132372e302e302e3200043500000018afabcafe0000000213531d870000000800000001000000000000000100000001000000140000000000010020000000000001010000000000
5

Interoperability

Why should RMI be run over IIOP?

If RMI uses it's own protocol, it is more transparently integrated with Java.

IIOP must be used if a Java program wants to interact with another CORBA program, e.g., written in C++. This is pursued further in the next chapter.

RMI over IIOP is simpler to program than a CORBA application; therefore, this might be the best approach as long as there is no specific CORBA interface definition to fulfill.

[However, I have only managed to let the JDK 1.3.1 RMI over IIOP use the ORBacus C++ nameserv ; none of the other interoperations has worked.]